﻿using Microsoft.Data.Edm;
using Microsoft.Data.OData;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.OData.Routing;
using System.Web.Http.OData.Routing.Conventions;
using System.Web.Http.Routing;
using WooCoo.Tools;

namespace WooCoo.DataPortal.WebHost
{
    /// <summary>继承 <see cref="T:System.Web.Http.Routing.IHttpRouteConstraint" />，只匹配OData路由.</summary>
    public class ODataServerRouteConstraint : IHttpRouteConstraint
    {
        /// <summary>Gets the OData path handler to use for parsing.</summary>
        public IODataPathHandler PathHandler
        {
            get;
            private set;
        }
        /// <summary>Gets the EDM model to use for parsing the path.</summary>
        public IEdmModel EdmModel
        {
            get;
            private set;
        }
        /// <summary>Gets the name of the route this constraint is associated with.</summary>
        public string RouteName
        {
            get;
            private set;
        }
        /// <summary>Gets the OData routing conventions to use for selecting the controller name.</summary>
        public IEnumerable<IODataRoutingConvention> RoutingConventions
        {
            get;
            private set;
        }
        /// <summary>Initializes a new instance of the <see cref="T:System.Web.Http.OData.Routing.ODataPathRouteConstraint" /> class.</summary>
        /// <param name="pathHandler">The OData path handler to use for parsing.</param>
        /// <param name="model">The EDM model to use for parsing the path.</param>
        /// <param name="routeName">The name of the route this constraint is associated with.</param>
        /// <param name="routingConventions">The OData routing conventions to use for selecting the controller name.</param>
        public ODataServerRouteConstraint(IODataPathHandler pathHandler, IEdmModel model, string routeName, IEnumerable<IODataRoutingConvention> routingConventions)
        {
            if (pathHandler == null)
            {
                throw Error.ArgumentNull("pathHandler");
            }
            if (model == null)
            {
                throw Error.ArgumentNull("model");
            }
            if (routeName == null)
            {
                throw Error.ArgumentNull("routeName");
            }
            if (routingConventions == null)
            {
                throw Error.ArgumentNull("routingConventions");
            }
            this.PathHandler = pathHandler;
            this.EdmModel = model;
            this.RouteName = routeName;
            this.RoutingConventions = routingConventions;
        }
        /// <summary>判断请求是否找得到相匹配的路由设定.</summary>
        /// <returns>匹配则返回true,并将controller设定至<paramref name="values"/>中.<br>
        /// 所有对ODataPortal的前缀请求都将转至ODataPortal</br>
        /// </returns>
        /// <param name="request">http请求.</param>
        /// <param name="route">路由,<see cref="DataPortalRoute"/>.</param>
        /// <param name="parameterName">参数名称.</param>
        /// <param name="values">参数值列表.</param>
        /// <param name="routeDirection">The route direction.</param>
        public virtual bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection)
        {
            if (request == null)
            {
                throw Error.ArgumentNull("request");
            }
            if (values == null)
            {
                throw Error.ArgumentNull("values");
            }
            if (routeDirection == HttpRouteDirection.UriResolution)
            {
                //是否根路径请求，则转向Excute
                if (request.RequestUri.AbsolutePath.EndsWith("ODataPortal", StringComparison.CurrentCultureIgnoreCase))
                {
                    values[ODataRouteConstants.Controller] = "ODataPortal";
                    return true;
                }
                object obj;
                if (values.TryGetValue(ODataRouteConstants.ODataPath, out obj))
                {
                    string text = obj as string;
                    if (text == null)
                    {
                        text = string.Empty;
                    }
                    
                    ODataPath oDataPath;
                    try
                    {
                        oDataPath = this.PathHandler.Parse(this.EdmModel, text);
                    }
                    catch (ODataException exception)
                    {
                        throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.NotFound, "ODataPathInvalid", exception));
                    }
                    if (oDataPath != null)
                    {
                        request.SetEdmModel(this.EdmModel);
                        request.SetODataPathHandler(this.PathHandler);
                        request.SetODataPath(oDataPath);
                        request.SetODataRouteName(this.RouteName);
                        request.SetODataRoutingConventions(this.RoutingConventions);
                        //values[ODataRouteConstants.Controller] = "ODataPortal";
                        if (!values.ContainsKey(ODataRouteConstants.Controller))
                        {
                            string text2 = this.SelectControllerName(oDataPath, request);
                            if (text2 != null)
                            {
                                values[ODataRouteConstants.Controller] = text2;
                            }
                        }
                        return true;
                    }
                }
                return false;
            }
            return true;
        }
        /// <summary>Selects the name of the controller to dispatch the request to.</summary>
        /// <returns>The name of the controller to dispatch to, or null if one cannot be resolved.</returns>
        /// <param name="path">The OData path of the request.</param>
        /// <param name="request">The request.</param>
        protected virtual string SelectControllerName(ODataPath path, HttpRequestMessage request)
        {
            foreach (IODataRoutingConvention current in this.RoutingConventions)
            {
                string text = current.SelectController(path, request);
                if (text != null)
                {
                    return text;
                }
            }
            return null;
        }
    }
}
